use std::{cell::RefCell, rc::Rc, sync::Mutex};

use probe_rs::{ config::{families, get_target_and_family_by_name, ChipFamily}, flashing::{ download_file_with_options, DownloadOptions, FlashProgress, Format, ProgressEvent}, probe::list::Lister, Permissions};
use serde::Serialize;
use tauri::Window;

use crate::error::{AppErr, ErrWrap};

mod device;

#[tauri::command]
pub fn chip_families() -> Vec<ChipFamily> {
    families()
}

#[tauri::command]
pub fn debug_info() -> Result<Vec<String>, AppErr> {
    let lister = Lister::new();
    let probes = lister.list_all();
    Ok( probes.into_iter().map(|item| item.identifier).collect() )
}

const EVENT_DOWNLOAD_INIT: &'static str = "download_init";
const EVENT_DOWNLOAD_PROG: &'static str = "download_prog";
const EVENT_DOWNLOAD_ERASE: &'static str = "download_erase";

#[derive(Debug, Serialize, Clone, Copy)]
struct ProgInfo {
    size: u32,
    duration: u32,
}

#[tauri::command]
pub async fn download(id: String, file_path: String, target: String, window: Window) -> Result<(), AppErr> {
    tokio::task::spawn_blocking(move || {
        download_impl(id, file_path, target, window)
    }).await??;
    Ok(())
}


fn download_impl(id: String, file_path: String, target: String, window: Window) -> Result<(), AppErr> {
    println!("{} {} {}", id, file_path, target);
    let probe = device::open(&id)?;

    let mut session = probe.attach(target, Permissions::default())?;
    let mut opt = DownloadOptions::default();
    opt.verify = true;
    opt.progress = Some(FlashProgress::new( move |event| {
        match event {
            ProgressEvent::StartedProgramming { length } => {
                _ = window.emit(EVENT_DOWNLOAD_INIT, length);
            }

            ProgressEvent::SectorErased { size, time } => {
                let info = ProgInfo {
                    size: size as u32,
                    duration: time.as_millis() as u32,
                };
                _ = window.emit(EVENT_DOWNLOAD_ERASE, info);
            },

            ProgressEvent::PageProgrammed { size, time } => {
                let info = ProgInfo {
                    size,
                    duration: time.as_millis() as u32,
                };
                _ = window.emit(EVENT_DOWNLOAD_PROG, info);
            },

            
            _ => {}
        };
    }));
    download_file_with_options(&mut session, file_path, Format::Hex, opt).wrap()?;
    let cores = session.list_cores();
    for info in cores {
        let mut core = session.core(info.0)?;
        core.reset()?;
    }

    Ok(())
}